﻿/*
Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/

(function () {
    var plugin = {
        name: 'highlight',
        className: 'plugin_',
        console: window.console || { log: function (content) { window.external.console(content) } },
        instances: {
            add: function (editor) {
                this[editor.name] = new highlight(editor);
            }
        },
        keys: { BACKSPACE: 8, TAB: 9, CLEAR: 12, ENTER: 13, SHIFT: 16, CTRL: 17, ALT: 18, PAUSE: 19, CAPS_LOCK: 20, ESCAPE: 27, SPACE: 32, PAGE_UP: 33, PAGE_DOWN: 34, END: 35, HOME: 36, LEFT_ARROW: 37, UP_ARROW: 38, RIGHT_ARROW: 39, DOWN_ARROW: 40, INSERT: 45, DELETE: 46, HELP: 47, LEFT_WINDOW: 91, RIGHT_WINDOW: 92, SELECT: 93, NUMPAD_0: 96, NUMPAD_1: 97, NUMPAD_2: 98, NUMPAD_3: 99, NUMPAD_4: 100, NUMPAD_5: 101, NUMPAD_6: 102, NUMPAD_7: 103, NUMPAD_8: 104, NUMPAD_9: 105, NUMPAD_MULTIPLY: 106, NUMPAD_PLUS: 107, NUMPAD_ENTER: 108, NUMPAD_MINUS: 109, NUMPAD_PERIOD: 110, NUMPAD_DIVIDE: 111, F1: 112, F2: 113, F3: 114, F4: 115, F5: 116, F6: 117, F7: 118, F8: 119, F9: 120, F10: 121, F11: 122, F12: 123, F13: 124, F14: 125, F15: 126, NUM_LOCK: 144, SCROLL_LOCK: 145 },
        getHighLight: function (editor) {
            return this.instances[editor.name];
        },
        addInstance: function (editor) {
            editor.on('contentDom', function () {
                plugin.instances.add(editor);
            });
            editor.on('destroy', function (evt) {
                var editor = evt.editor,
                    hl_instance = plugin.getHighLight(editor);
                if (!hl_instance)
                    return;
                delete plugin.instances[editor.name];
                hl_instance.destroy(true);
            });
            editor.on('afterSetData', function () {
                if (plugin.isScaytEnabled(editor)) {
                    window.setTimeout(function () {
                        var hl_instance = plugin.getHighLight(editor);
                        hl_instance && hl_instance.refresh();
                    }, 10);
                }
            });
            editor.on('beforeCommandExec', function (evt) {
                // Disable SCAYT before Source command execution.
                if ((evt.data.name == 'source' || evt.data.name == 'newpage') && editor.mode == 'wysiwyg') {
                    var hl_instance = plugin.getHighLight(editor);
                    if (hl_instance) {
                        hl_instance.destroy(true);
                        delete plugin.instances[editor.name];
                    }
                }
                    // Catch on source mode switch off (#5720)
                else if (evt.data.name == 'source' && editor.mode == 'source') {

                }
            });
            editor.on('afterCommandExec', function (evt) {
                if (editor.mode == 'wysiwyg' && (evt.data.name == 'undo' || evt.data.name == 'redo'))
                    window.setTimeout(function () { plugin.getHighLight(editor).refresh(); }, 10);
            });
            editor.on('insertElement', function () {
                var hl_instance = plugin.getHighLight(editor);
                if (CKEDITOR.env.ie)
                    editor.getSelection().unlock(true);

                // Return focus to the editor and refresh SCAYT markup (#5573).
                window.setTimeout(function () {
                    hl_instance.focus();
                    hl_instance.refresh();
                }, 10);
            }, this, null, 50);

            editor.on('insertHtml', function () {
                var hl_instance = plugin.getHighLight(editor);
                if (CKEDITOR.env.ie)
                    editor.getSelection().unlock(true);

                // Return focus to the editor (#5573)
                // Refresh SCAYT markup
                window.setTimeout(function () {
                    hl_instance.focus();
                    hl_instance.refresh();
                }, 10);
            }, this, null, 50);

            var dataProcessor = editor.dataProcessor,
            htmlFilter = dataProcessor && dataProcessor.htmlFilter;

            if (htmlFilter) {
                htmlFilter.addRules({
                    elements: {
                        span: function (element) {
                            if (element.attributes['data-highlight-key']) {
                                delete element.name; // Write children, but don't write this node.
                                return element;
                            }
                        },
                        img: function (element) {
                            if (element.attributes['data-highlight-key']) {
                                delete element.name;
                                return element;
                            }
                        }
                    }
                });
            }


            // Override Image.equals method avoid CK snapshot module to add SCAYT markup to snapshots. (#5546)
            //var undoImagePrototype = CKEDITOR.plugins.undo.Image.prototype;
            //undoImagePrototype.equals = CKEDITOR.tools.override(undoImagePrototype.equals, function (org) {
            //    return function (otherImage) {
            //        var thisContents = this.contents,
            //        otherContents = otherImage.contents;
            //        var hl_instance = plugin.getHighLight(this.editor);
            //        // Making the comparison based on content without SCAYT word markers.
            //        if (hl_instance) {
            //            // scayt::reset might return value undefined. (#5742)
            //            this.contents = hl_instance.reset(thisContents) || '';
            //            otherImage.contents = hl_instance.reset(otherContents) || '';
            //        }

            //        var retval = org.apply(this, arguments);

            //        this.contents = thisContents;
            //        otherImage.contents = otherContents;
            //        return retval;
            //    };
            //});

            if (editor.document)
                this.instances.add(editor);
        }
    };
    var highlight = CKEDITOR.plugins.highlight = function (editor) {
        this.editor = editor;
        this.init();
    };
    plugin.className += plugin.name;
    CKEDITOR.tools.extend(highlight, plugin);

    CKEDITOR.tools.extend(highlight.prototype, {
        _keydown: false,
        _keypress: false,
        _lastChecking: 0,
        _innerHTML: '',
        span: new CKEDITOR.dom.element('span'),
        config: [],
        document: null,
        containerNode: null,
        init: function () {
            /// <summary>高亮初始化</summary>
            var t = this;

            t.document = t.editor.document;
            t.containerNode = t.document.getBody();

            //测试代码
            var body = t.containerNode;
            //body.append(new CKEDITOR.dom.text('aa阿狄森病aa公'));
            //body.append(new CKEDITOR.dom.text(''));
            //body.append(new CKEDITOR.dom.text('私ccc阿狄森氏病aα受体阻滞剂'));

            t.span.data(plugin.name, "on");

            //存储最后一次数据后的html内容，用来判断内容是否发生变化
            t._innerHTML = t.containerNode.getHtml();
            try {
                t._innerHTML = window.external.Highlight(window.external.ClearControlChr(t._innerHTML), CKEDITOR.plugins.formattag.enable);
            } catch (e) {

            }
            if (t._innerHTML != t.containerNode.getHtml()) {
                t.containerNode.setHtml(t._innerHTML);
            }
            t._innerHTML == t.containerNode.getHtml();
            var evt;
            if (CKEDITOR.env.webKit) {
                evt = { "onkeydown": "onBeforeEvt", "onkeyup": "onAfterEvt", "onmousedown": "onMouseDown" };
            } else {
                evt = { "onkeypress": "onBeforeEvt", "onkeyup": "onAfterEvt", "onmousedown": "onMouseDown" };
            }
            if (CKEDITOR.env.gecko || CKEDITOR.env.webKit || CKEDITOR.env.ie) {
                evt.onpaste = "onAfterEvt";
                evt.oncut = "onAfterEvt";
            }
            if (CKEDITOR.env.ie) {
                evt["onkeydown"] = "onBeforeEvtIe";
            }
            for (x in evt) {
                t.editor.document.on(x.substr(2), t[evt[x]], t);
            }
        },
        setContent: function (func) {
            var t = this;
            var range = t.getRange();
            if (range) {
                range.optimizeBookmark();
                var bookmark = range.createBookmark(true);
                func.apply(t, [bookmark]);
                if (bookmark) {
                    t.containerNode.$.setActive();
                    range.moveToBookmark(bookmark);
                }
            }
            try {
                t._innerHTML = t.containerNode.getHtml();
            }
            catch (e) {
            }
        },
        getParentEx: function () {
            /// <summary>查找当前选择的节点的所有高亮的父节点</summary>
            var hls = { isHighLight: false, hls: [] };
            var t = this;
            var node = t.getSelectionNode();
            while (node && !t.containerNode.equals(node)) {
                if (t.isHLNode(node)) {
                    hls.hls.push(node);
                    hls.isHighLight = true;
                }
                node = node.getParent();
            }
            return hls;
        },
        isHLNode: function (node) {
            return (node && node.type == CKEDITOR.NODE_ELEMENT && node.data(plugin.name + '-key'));
        },
        getSelectionNode: function () {
            var t = this;
            var range = t.getRange();
            return range.startContainer;
        },
        getRange: function () {
            var t = this;
            var sel = t.editor.getSelection();
            var ranges = sel && sel.getRanges();
            return ranges && ranges.length && ranges[0];
        },
        getKeyMoving: function () {
            /// <summary>获取所有移动光标的按键</summary>
            var dk = plugin.keys;
            return [dk.HOME, dk.UP_ARROW, dk.DOWN_ARROW, dk.LEFT_ARROW, dk.RIGHT_ARROW, dk.END, dk.PAGE_UP, dk.PAGE_DOWN, 93];
        },
        remove: function (node) {
            if (!arguments.length) {
                return;
            }
            if (!CKEDITOR.tools.isArray(node)) {
                node = [node];
            }
            if (node.length < 500) {
                for (var i = 0; i < node.length; i++) {
                    node[i].remove(true);
                }
            }
        },
        getParent: function (node, nodeName, attr, root) {
            var t = this;
            root = root || t.containerNode;
            nodeName = nodeName ? new RegExp("^(?:" + nodeName + ")$") : null;
            while (node && !node.equals(root)) {
                if (nodeName && nodeName.test(node.$.nodeName.toUpperCase())) {
                    return node;
                }
                if (attr) {
                    for (var k in attr) {
                        if (node.type == CKEDITOR.NODE_ELEMENT && node.getAttribute(k) !== null && attr[k] === null) {
                            return node;
                        }
                        if ((attr[k] !== null && node.getAttribute(k) !== null && !(node.getAttribute(k) === false)) ? !attr[k] : attr[k]) {
                            return node;
                        }
                    }
                }
                node = node.getParent();
            }
            return false;
        },
        getHLNode: function (node) {
            var t = this;
            if (!arguments.length) {
                try {
                    node = t.getSelectionNode();
                }
                catch (e) {
                    return null;
                }
            }
            var attName = "data-" + plugin.name + "-key";
            node = t.getParent(node, null, { attName: null });
            return node;
        },
        onBeforeEvtIe: function (evt) {
            //plugin.console.log("onBeforeEvtIe");
            var t = this;
            t._keydown = true;
            if (evt.data.getKey() == 13) {
                var hls = t.getParentEx();
                if (hls.isHighLight) {
                    var book = new CKEDITOR.dom.text("", t.document);
                    var rng = t.getRange();
                    if (rng.startContainer.type == CKEDITOR.NODE_TEXT && rng.startContainer.substring(rng.endOffset).length == 0) {
                        var p = rng.startContainer.getParent();
                        if (!p.equals(t.containerNode)) {
                            book.insertAfter(p);
                        }
                    } else {
                        rng.insertNode(book);
                        t.remove(hls.hls);
                    }

                    rng.setEndAfter(book);
                    rng.collapse(false);
                    rng.select();
                }
            }
        },
        onBeforeEvt: function (evt) {
            plugin.console.log("onBeforeEvt");
            //这里首先需要判断除开正常的输入按键，粘帖操作意外的按键操作才能继续；判断前后节点是否是高亮节点，如果是的话需要移除前后的高亮节点
            var t = this, keyCode = evt.data.getKey(), keyStore = evt.data.getKeystroke, movings = t.getKeyMoving(), evt$ = evt.data.$;
            t._keypress = true;
            if ((evt$.ctrlKey && keyCode != 86) || evt$.altKey || (!CKEDITOR.env.gecko && evt$.shiftKey && !evt$.charCode)) {
                return;
            }
            if ((evt$.altKey && keyCode == 18) || CKEDITOR.tools.indexOf(movings, keyCode) != -1 || keyCode == 9) {
                return;
            }
            var hls = t.getParentEx();
            if (hls.isHighLight) {
                var book = new CKEDITOR.dom.text("", t.document);
                var rng = t.getRange();
                rng.insertNode(book);
                t.remove(hls.hls);
                rng.setEndAfter(book);
                rng.collapse(false);
                rng.select();
            }
        },
        onAfterEvt: function (evt) {
            plugin.console.log("onAfterEvt");

            var t = this, keyCode = evt.data.getKey(), keyStore = evt.data.getKeystroke(), movings = t.getKeyMoving(), evt$ = evt.data.$;
            var keydown = t._keydown;
            var keypress = t._keypress;
            t._keydown = false;
            t._keypress = false;
            plugin.console.log("iskeydown" + keydown);
            plugin.console.log("iskeypress" + keypress);
            //如果没有同时触发keydown和keypress事件则不执行事件处理这意味着没有输入有效的字符。可能只是输入了控制字符而已
            //但是控制字符中的剪切、粘贴、删除事件需要处理
            //另一种情况是在嵌入式输入法里面，例如：微软的拼音等输入法。
            //处于输入模式时输入法会向编辑器中插入一个空格来占位，
            //但是这个时候是不应该进行高亮替换的。
            //我们需要在这里记录
            if (keydown && !keypress && (
                (keyCode >= 65 && keyCode <= 90) || //a-z
                (keyCode >= 48 && keyCode <= 57) || //0! 2@ 3# 4$ 5% 6^ 7& 8* 9( 0)
                (keyCode >= 186 && keyCode <= 222) || //;: =+ ,< -_ .> /? `~ [{ \| ]} '"
                (keyCode == 32) //空格
                )) {
                keypress == t._innerHTML == t.containerNode.getHtml();
                if (t._lastChecking <= 0 && t._innerHTML != t.containerNode.getHtml()) {
                    var selNode = t.getSelectionNode();

                    t._innerHTML = t.containerNode.getHtml();
                    plugin.console.log("进入输入法状态：" + t._innerHTML);
                    t._lastChecking = window.setInterval(function () {
                        if (t._innerHTML != t.containerNode.getHtml()) {
                            plugin.console.log("修正输入法内容");
                            t._innerHTML = t.containerNode.getHtml();
                            var sel = t.document.$.selection;
                            var rng = sel.createRange();
                            var rect = rng.getBoundingClientRect();
                            var left = rng.boundingLeft;
                            var top = rng.boundingTop; // +rng.boundingHeight;
                            try {
                                t._innerHTML = window.external.ResetHighLight(t._innerHTML, CKEDITOR.plugins.formattag.enable);
                            } catch (e) {
                                plugin.console.log("重置高亮关键字错误！");
                            }
                            if (t._innerHTML != t.containerNode.getHtml()) {
                                t.containerNode.setHtml(t._innerHTML);
                                rng.moveToPoint(left, top);
                                rng.select();
                            }
                            window.clearInterval(t._lastChecking);
                            t._innerHTML == t.containerNode.getHtml();
                            t._lastChecking = 0;
                        } else {
                            plugin.console.log("输入法未更改:" + t._innerHTML);
                        }
                    }, 3000);
                    return;
                }
            } else if (t._lastChecking > 0) {
                window.clearInterval(t._lastChecking);
                t._lastChecking = 0;
            }
            if (!(keydown && keypress) &&
                evt.name != "cut" &&
                evt.name != "paste" &&
                keyStore != CKEDITOR.CTRL + 86 && //CTRL+V
                keyStore != CKEDITOR.CTRL + 88 && //CTRL+X
                keyStore != CKEDITOR.SHIFT + 45 && //SHIFT+INSERT
                keyStore != 8 && //BackSpace
                keyStore != 46 //Delete
                ) {
                //没有输入可打印字符，并且也不是剪切和粘贴 也不是删除键
                plugin.console.log("不处理按键：" + keyStore);
                return;
            }
            //debugger;
            if (t._innerHTML == t.containerNode.getHtml()) {
                return;
            }

            var range = new CKEDITOR.dom.range(t.document), oRange = t.getRange();
            //var hlParent = this.getHLNode();
            //window.external.console.log("父节点：" + hlParent.toString());
            //if (hlParent) {
            //    hlParent.remove(false);
            //    //window.external.console.log("删除父节点：" + hlParent.getOuterHtml());
            //}
            var selNode = t.getSelectionNode();
            // plugin.console.log(selNode.getParent().getOuterHtml());
            if (selNode.type != CKEDITOR.NODE_TEXT) {
                //这里有可能选择了一个父节点的末尾或者两个元素之间
                if (oRange.startOffset) {
                    selNode = selNode.getChild(oRange.startOffset - 1);
                }

                //plugin.console.log("当前选中的节点不是文本节点");
                //return;
            }
            //var selParentNode = selNode.getParent();
            //if (CKEDITOR.plugins.formattag.isSpaceNode(selParentNode)) {
            //    alert(selParentNode.getOuterHtml());
            //    selParentNode.remove(true);
            //    return;
            //}
            var startNode, endNode = startNode = selNode;
            var textContent = selNode.getText();
            var htmlContent = selNode.getText();
            var bookmarkOffset = oRange.startOffset;
            do {
                //查找光标前面的所有文本节点，包括已经高亮的节点
                var prev = startNode.hasPrevious() && startNode.getPrevious();

                if (prev && prev.type == CKEDITOR.NODE_TEXT) {
                    startNode = prev;
                    var tempText = prev.getText();
                    var tempHtml = tempText;
                    if (prev.type == CKEDITOR.NODE_ELEMENT) {
                        tempHtml = prev.getOuterHtml();
                    }
                    textContent = tempText + textContent;
                    bookmarkOffset += tempText.length;
                    htmlContent = tempHtml + htmlContent;
                } else {
                    prev = null;
                }
            } while (prev != null);
            do {
                //查找光标后面的所有文本节点，包括已经高亮的节点
                var next = endNode.hasNext() && endNode.getNext();
                if (next && next.type == CKEDITOR.NODE_TEXT) {
                    endNode = next;
                } else {
                    next = null;
                }
            } while (next != null);
            var p = selNode.getParent();
            range.setStart(startNode, 0);
            var orgLength = selNode.getText().length; //记录选中节点的长度，用于后面判断是否被分割过。
            range.setEnd(endNode, endNode.getText().length);
            if (CKEDITOR.env.ie) {
                var replace = false;
                try {
                    replace = window.external.EditorHighLight(t.document.$, startNode.$, endNode.$, CKEDITOR.plugins.formattag.enable);

                } catch (e) {

                }
                if (replace) {
                    t._innerHTML = t.containerNode.getHtml();
                    oRange = t.getRange();
                    var pNode = this.getHLNode();
                    if (pNode) {
                        oRange.setEndAfter(pNode);
                        oRange.collapse(false);
                        oRange.select();
                    }
                }
                return;
            }
            this.setContent(function (bookmark) {
                //这里创建了标签后把TEXT节点分割了。所以这里获取内容会出错了。所以这里需要判断是否分割过Text节点
                if (bookmark && endNode.equals(selNode) && orgLength > selNode.getText().length) {
                    //当选中的节点是选择范围的最后一个节点时，需要修正选择的范围
                    endNode = t.document.getById(bookmark.startNode).getNext(); //书签的下一个节点
                    range.setEnd(endNode, endNode.getText().length);
                }
                var fragment = range.cloneContents();
                var children = fragment.getChildren();
                var text = "", markIndex = -1, markHTML = "";
                for (var i = 0; i < children.count() ; i++) {
                    var child = children.getItem(i);
                    if (child.type == CKEDITOR.NODE_TEXT || (child.type == CKEDITOR.NODE_ELEMENT && !child.data("cke-bookmark"))) {
                        text += child.getText();
                    }
                    if (child.type == CKEDITOR.NODE_ELEMENT && child.data("cke-bookmark")) {
                        markIndex = text.length;
                        markHTML = child.getOuterHtml();
                    }
                    //child.remove(true);
                }
                var matchs = [];
                for (var i = 0; i < t.config.length; i++) {
                    var key = t.config[i];
                    while ((match = key.regexp.exec(text)) != null) {
                        matchs.push({ start: match.index, end: match.lastIndex, key: key });
                    }
                }
                if (matchs.length == 0) {
                    return;
                }
                matchs.sort(function (a, b) {
                    /// <summary>倒序排列数组</summary>
                    if (a.start < b.start) {
                        return -1;
                    }
                    if (a.start == b.start) {
                        return 0;
                    }
                    if (a.start > b.start) {
                        return 1;
                    }
                });
                fragment = new CKEDITOR.dom.element('div', t.document);
                var lastIndex = 0;

                for (var i = 0; i < matchs.length; i++) {
                    var match = matchs[i];
                    if (match.start > lastIndex) {
                        var textNode = new CKEDITOR.dom.text(text.substring(lastIndex, match.start))

                        var markNode = t.span.clone();
                        markNode.append(textNode);
                        fragment.append(markNode);
                        if (markIndex > lastIndex && markIndex < match.start) {
                            t.mark(textNode, markIndex - lastIndex, bookmark);
                        }
                        markNode.remove(true);
                    }
                    var sNode = t.span.clone();
                    sNode.setText(text.substring(match.start, match.end));
                    sNode.setStyles({ 'color': match.key.ForeColor, 'background-color': match.key.BackColor });
                    sNode.data(plugin.name + '-key', match.key.Key);
                    if (match.key.Suggest) {
                        sNode.data(plugin.name + '-suggest', match.key.Suggest);
                    }
                    fragment.append(sNode);

                    if (markIndex > match.start && markIndex < match.end) {
                        t.mark(sNode, markIndex - match.start, bookmark);
                    }
                    lastIndex = match.end;
                }

                if (lastIndex < text.length) {
                    var tempNode = new CKEDITOR.dom.text(text.substring(lastIndex));

                    var markNode = t.span.clone();
                    markNode.append(tempNode);
                    fragment.append(markNode);
                    if (markIndex > lastIndex && markIndex < match.start) {
                        t.mark(textNode, markIndex - lastIndex, bookmark);
                    }
                    markNode.remove(true);

                    //标签在最后一段未匹配的文字里面
                    if (markIndex > lastIndex) {// && markIndex < text.length
                        t.mark(tempNode, markIndex - lastIndex, bookmark);

                    }
                    markNode.remove(true);
                }



                //这里通过fragment创建的bookmark不能和文档上的bookmark是不同的。
                range.deleteContents();
                range.insertNode(fragment);
                fragment.remove(true);
                //debugger;
                //处理和替换后再替换页面内容。
            });
            //debugger;
            //range && t.onInput(range);
        },
        mark: function (node, index, bm) {
            var t = this;
            var textRange = new CKEDITOR.dom.range(t.document);
            textRange.setStart(node, index);
            textRange.collapse(true);
            var mark = textRange.createBookmark();
            mark.startNode.setAttribute("id", bm.startNode);
        },
        onInput: function (range) {
            /// <summary>当用户输入信息后修正光标位置</summary>
            //debugger;
            var startNode = range.startContainer;
            var endNode = range.endContainer;
            var startOffset = range.startOffset;
        },
        onMouseDown: function () {
        },
        destroy: function () {
        },
        focus: function () {
        },
        refresh: function () {
        },
        reset: function () { }
    });
    // Context menu constructing.
    var addButtonCommand = function (editor, buttonName, buttonLabel, commandName, command, menugroup, menuOrder) {
        editor.addCommand(commandName, command);

        // If the "menu" plugin is loaded, register the menu item.
        editor.addMenuItem(commandName,
            {
                label: buttonLabel,
                command: commandName,
                group: menugroup,
                order: menuOrder
            });
    };

    CKEDITOR.plugins.add(plugin.name, {
        //requires: ['tabletools', 'formattag'],
        init: function (editor) {
            // 当文本粘帖到编辑器中，删除 span[data-scaytid]
            var dataFilter = editor.dataProcessor && editor.dataProcessor.dataFilter;
            var dataFilterRules = {
                elements: {
                    span: function (element) {
                        var attrs = element.attributes;
                        if (attrs && attrs['data-highlight-key'])
                            delete element.name;
                    },
                    img: function (element) {
                        if (element.attributes['data-highlight-key']) {
                            delete element.name;
                        }
                    }
                }
            };
            dataFilter && dataFilter.addRules(dataFilterRules);

            var mainSuggestions = {};
            if (editor.contextMenu && editor.addMenuItems) {
                editor.addMenuGroup("highlight_suggest", 1);
                editor.contextMenu.addListener(function (element, selection) {
                    if (!element || !element.getAttribute('data-highlight-suggest')) {
                        return null;
                    }

                    // Remove unused commands and menuitems
                    for (m in mainSuggestions) {
                        delete editor._.menuItems[m];
                        delete editor.commands[m];
                    }
                    mainSuggestions = {};

                    var items_suggestion = element.getAttribute('data-highlight-suggest') || '';
                    items_suggestion = items_suggestion.split('|');
                    for (var i = 0, l = items_suggestion.length; i < l; i += 1) {
                        var commandName = 'highlight_suggestion_' + items_suggestion[i].replace(' ', '_');
                        var exec = (function (el, s) {
                            return {
                                exec: function () {
                                    var text = new CKEDITOR.dom.text(s, editor.document);
                                    text.replace(el);
                                }
                            };
                        })(element, items_suggestion[i]);

                        addButtonCommand(editor, 'button_' + commandName, items_suggestion[i],
                                    commandName, exec, 'highlight_suggest', i + 1);
                        mainSuggestions[commandName] = CKEDITOR.TRISTATE_OFF;
                    }
                    return mainSuggestions;
                });
            }

            editor.on('instanceReady', function () {
                plugin.addInstance(editor);
            });
        },
        afterInit: function (editor) {
            // 防止标记在清除格式时被删除
            var elementsPathFilters,
                    filter = function (element) {
                        if (element.hasAttribute('data-highlight-key'))
                            return false;
                    };

            if (editor._.elementsPath && (elementsPathFilters = editor._.elementsPath.filters))
                elementsPathFilters.push(filter);
            editor.addRemoveFormatFilter && editor.addRemoveFormatFilter(filter);
        }
    });
})();
